/*
 * Hosted Game: Zombies
 * D1st0rt, SSCE Hyperspace
 * License: MIT/X11
 * Created: 2009-02-15
 *
 * Two teams to start: humans and zombies. If a human dies, they become
 * a zombie. Zombies win when there are no humans left, humans win if
 * they can survive long enough for an "extraction".
 */

///////////////////////////////////////////////////////////////////////
// Includes
///////////////////////////////////////////////////////////////////////

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "asss.h"
#include "hgcore.h"
#include "optparser.h"

///////////////////////////////////////////////////////////////////////
// Interfaces
///////////////////////////////////////////////////////////////////////

local Imodman *mm;
local Ilogman *lm;
local Ichat *chat;
local Igame *game;
local Iplayerdata *pd;
local Iarenaman *aman;
local Iconfig *config;
local Imainloop *ml;
local Ihgcore *hgcore;
local Ioptparser *parser;

///////////////////////////////////////////////////////////////////////
// Defines/Types
///////////////////////////////////////////////////////////////////////

#define HGR_HUMANS_ESCAPED 1
#define HUMAN_FREQ 0
#define ZOMBIE_FREQ 1

typedef struct hg_zombies_adata
{
    int cfg_ZombieShip;
    int cfg_ExtractionWait;
} hg_zombies_adata;

///////////////////////////////////////////////////////////////////////
// Globals
///////////////////////////////////////////////////////////////////////

local pthread_mutex_t globalmutex;
local int adkey;

EXPORT const char info_hg_zombies[] = "09.05.12 by D1st0rt <d1st0rter@gmail.com>";

///////////////////////////////////////////////////////////////////////
// Interface Prototypes
///////////////////////////////////////////////////////////////////////
local int OnPrepare(Arena *a, Player *host, const char *params);
local int OnStart(Arena *a, Player *host);
local void OnGameover(Arena *a, int how);
local void OnCleanup(Arena *a);
local void OnTeamCreate(Arena *a, HGTeam *team);
local void OnTeamDestroy(Arena *a, HGTeam *team);
local int SizeOfTeamData();
local void TeamInfoString(HGTeam *team, char *str, int buflen);
///////////////////////////////////////////////////////////////////////
// Prototypes
///////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////
// Callbacks
///////////////////////////////////////////////////////////////////////
local void PlayerKill(Arena *a, Player *k, Player *d, int bty, int flags, int greens);

///////////////////////////////////////////////////////////////////////
// Timers
///////////////////////////////////////////////////////////////////////
local int ExtractionAvailable(void *a);

///////////////////////////////////////////////////////////////////////
// Commands
///////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////
// Entry Point
///////////////////////////////////////////////////////////////////////
local Ihostedgame interface =
{
    INTERFACE_HEAD_INIT(I_HOSTEDGAME, "hg_zombies")

    OnPrepare,
    OnStart,
    OnGameover,
    OnCleanup,
    OnTeamCreate,
    OnTeamDestroy,
    SizeOfTeamData,
    TeamInfoString
};

EXPORT int MM_hg_zombies(int action, Imodman *mm_, Arena *arena)
{
    if (action == MM_LOAD)
    {
        // Global interfaces
        mm = mm_;
        chat    = mm->GetInterface(I_CHAT,       ALLARENAS);
        pd      = mm->GetInterface(I_PLAYERDATA, ALLARENAS);
        aman    = mm->GetInterface(I_ARENAMAN,   ALLARENAS);
        game    = mm->GetInterface(I_GAME,       ALLARENAS);
        lm      = mm->GetInterface(I_LOGMAN,     ALLARENAS);
        config  = mm->GetInterface(I_CONFIG,     ALLARENAS);
        ml      = mm->GetInterface(I_MAINLOOP,   ALLARENAS);
        hgcore  = mm->GetInterface(I_HGCORE,     ALLARENAS);
        parser  = mm->GetInterface(I_OPTPARSER,  ALLARENAS);

        if (!chat || !pd || !aman || !game || !lm || !config || !ml || !hgcore || !parser)
            return MM_FAIL;

        // Data
        adkey = aman->AllocateArenaData(sizeof(hg_zombies_adata));
        if (adkey == -1) return MM_FAIL;

        // Locking
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        pthread_mutex_init(&globalmutex, &attr);
        pthread_mutexattr_destroy(&attr);

        return MM_OK;
    }
    else if (action == MM_UNLOAD)
    {
        // Locking
        pthread_mutex_destroy(&globalmutex);

        // Data
        aman->FreeArenaData(adkey);

        // Global interfaces
        mm->ReleaseInterface(parser);
        mm->ReleaseInterface(hgcore);
        mm->ReleaseInterface(ml);
        mm->ReleaseInterface(config);
        mm->ReleaseInterface(lm);
        mm->ReleaseInterface(game);
        mm->ReleaseInterface(aman);
        mm->ReleaseInterface(pd);
        mm->ReleaseInterface(chat);

        return MM_OK;
    }

    else if (action == MM_ATTACH)
    {
        // Interface registration
        mm->RegInterface(&interface, arena);
        hgcore->AttachGame(arena, I_HOSTEDGAME, "hg_zombies");

        //Data
        hg_zombies_adata *ad = P_ARENA_DATA(arena, adkey);

        /* cfghelp: Zombies:ZombieShip, arena, int, def: SHIP_SPIDER, mod: hgcore
         * Ship to set humans in when they die. */
        ad->cfg_ZombieShip = config->GetInt(arena->cfg, "Zombies", "ZombieShip", SHIP_SPIDER);
        /* cfghelp: Zombies:ExtractionWait, arena, int, def: 15, mod: hgcore
         * How long humans need to survive to win. */
        ad->cfg_ExtractionWait = config->GetInt(arena->cfg, "Zombies", "ExtractionWait", 15);

        parser->AddOption("hg_zombies", "zship", "s", "zship", "int");
        parser->AddOption("hg_zombies", "time", "t", "time", "int");

        //Attached interfaces

        //Callbacks
        mm->RegCallback(CB_KILL, PlayerKill, arena);

        //Timers

        //Commands


        return MM_OK;
    }
    else if (action == MM_DETACH)
    {
        //Data
        parser->FreeGroup("hg_zombies");

        //Timers

        //Callbacks
        mm->UnregCallback(CB_KILL, PlayerKill, arena);

        //Commands

        // Interface registration
        if(!hgcore->DetachGame(arena))
        {
            return MM_FAIL;
        }

        if (mm->UnregInterface(&interface, arena))
        {
            return MM_FAIL;
        }

        return MM_OK;
    }

    return MM_FAIL;
}

///////////////////////////////////////////////////////////////////////
// Interface Functions
///////////////////////////////////////////////////////////////////////
local int OnPrepare(Arena *a, Player *host, const char *params)
{
    chat->SendArenaMessage(a, "New game of zombies beginning! Die and become a zombie.");
    chat->SendArenaMessage(a, "If you can live long enough for an extraction, you win!");

    hg_zombies_adata *ad = P_ARENA_DATA(a, adkey);

    parser->Parse("hg_zombies", params);

    int zship;
    int time;

    if(parser->GetInt("hg_zombies", "zship", &zship))
    {
        if(zship > 0 && zship < 9)
        {
            ad->cfg_ZombieShip = zship;
            chat->SendMessage(host, "Zombie ship set to %d.", zship);
        }
    }

    if(parser->GetInt("hg_zombies", "time", &time))
    {
        if(time > 0)
        {
            ad->cfg_ExtractionWait = time;
            chat->SendMessage(host, "Extraction coming after %d minute(s).", time);
        }
    }

    return 1;
}

local int OnStart(Arena *a, Player *host)
{
    hg_zombies_adata *ad = P_ARENA_DATA(a, adkey);
    ml->SetTimer(ExtractionAvailable, 6000*ad->cfg_ExtractionWait, 0, a, a);
    chat->SendArenaSoundMessage(a, SOUND_SCREAM, "THE ZOMBIES ARE COMING!");
    return 1;
}

local void OnGameover(Arena *a, int how)
{
    if(how == HGR_STOPPED || how == HGR_CANCELLED)
    {
        ml->ClearTimer(ExtractionAvailable, a);
    }
    else if(how == HGR_NONE_REMAINING)
    {
        chat->SendArenaMessage(a, "Zombies game canceled, there is nobody left.");
    }
    else if(how == HGR_NATURAL)
    {
        chat->SendArenaMessage(a, "The humans are all dead! Zombies win!");
        ml->ClearTimer(ExtractionAvailable, a);
    }
    else if(how == HGR_HUMANS_ESCAPED)
    {
        chat->SendArenaSoundMessage(a, SOUND_HALLELLULA, "Some of the humans escaped!");
        Player *p;
        Link *link;

        pd->Lock();
        FOR_EACH_PLAYER(p)
        {
            if(p->arena == a && p->p_freq == HUMAN_FREQ)
            {
                chat->SendArenaMessage(a, "%s made it to safety!", p->name);
            }
        }
        pd->Unlock();
    }
}

local void OnCleanup(Arena *a)
{
    //nothing required
}

local void OnTeamCreate(Arena *a, HGTeam *team)
{
    //nothing required
}

local void OnTeamDestroy(Arena *a, HGTeam *team)
{
    //nothing required
}

local int SizeOfTeamData()
{
    return 0;
}

local void TeamInfoString(HGTeam *team, char *str, int buflen)
{
    int offset = strlen(str);
    char *buf = (char *)(((char*)str)+offset);
    sprintf(buf, " (%s)", team->Freq == HUMAN_FREQ ? "HUMANS" : "ZOMBIES");
}
///////////////////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////
// Callbacks
///////////////////////////////////////////////////////////////////////
local void PlayerKill(Arena *a, Player *k, Player *d, int bty, int flags, int greens)
{
    hg_zombies_adata *ad = P_ARENA_DATA(a, adkey);
    if(hgcore->GameStatus(a) == HGS_ACTIVE)
    {
        if(d->p_freq == HUMAN_FREQ)
        {
            chat->SendArenaSoundMessage(a, SOUND_SCREAM, "%s died and is now a zombie!", d->name);
            game->SetFreqAndShip(d, ad->cfg_ZombieShip, ZOMBIE_FREQ);
            hgcore->EliminatePlayer(a, d);
        }
    }
}

///////////////////////////////////////////////////////////////////////
// Timers
///////////////////////////////////////////////////////////////////////
local int ExtractionAvailable(void *a)
{
    //if the game is still going on, there must be humans left
    if(hgcore->GameStatus(a) == HGS_ACTIVE)
    {
        hgcore->EndGame(a, HGR_HUMANS_ESCAPED);
    }
    return FALSE;
}

///////////////////////////////////////////////////////////////////////
// Commands
///////////////////////////////////////////////////////////////////////

